'use strict';
//////////////////////////////////////////////////////////////////////////////
// 数字转换为简写汉字一到十
// =====================
// liuyp 2018年12月20日11:28:08
//////////////////////////////////////////////////////////////////////////////
const cnNumber = function(num){
    var i = 0, n=num+"", cn = ['\u96f6','\u4e00','\u4e8c','\u4e09','\u56db','\u4e94','\u516d','\u4e03','\u516b','\u4e5d'];
    for(;i<10;i++) n = n.replace(new RegExp(i, "g"),cn[i]); return n;
}
//////////////////////////////////////////////////////////////////////////////
// 数位补全(2位)
// @param num {int} 补全的数值 请自觉传入最多两位
// @returns {String} 处理后的字符串
// @description 如果传入的数值小于10，即位数只有1位，则在前面补充0
// =====================
// liuyp 2019年9月20日10:41:05
//////////////////////////////////////////////////////////////////////////////
const fillup2Digit = function (num) {
    let n = num.toString();
    return n[1] !== undefined ? n[0] + n[1]: '0' + n[0];
}
//////////////////////////////////////////////////////////////////////////////
// 数字转换为简写汉字读数
// =====================
// liuyp 2019年1月22日19:49:31
//////////////////////////////////////////////////////////////////////////////
const number2Chinese = (num) => {
    // 如果数字含有小数部分，那么可以将小数部分单独取出
    // 将小数部分的数字转换为字符串的方法：
    let chnNumChar = ['零', '一', '二', '三', '四', '五', '六', '七', '八', '九']
    let chnUnitSection = ['', '万', '亿', '万亿', '亿亿']
    let chnUnitChar = ['', '十', '百', '千']
    let numToChn = function (num) {
        let index = num.toString().indexOf('.')
        if (index === -1) {
            return ''
        }
        let str = num.toString().slice(index)
        let a = '点'
        for (let i = 1; i < str.length; i++) {
            a += chnNumChar[parseInt(str[i])]
        }
        return a || ''
    }
    // 定义在每个小节的内部进行转化的方法，其他部分则与小节内部转化方法相同
    function sectionToChinese (section) {
        let str = ''
        let chnstr = ''
        let zero = false
        let count = 0 // zero为是否进行补零， 第一次进行取余由于为个位数，默认不补零
        while (section > 0) {
            let v = section % 10 // 对数字取余10，得到的数即为个位数
            if (v === 0) { // 如果数字为零，则对字符串进行补零
                // 如果遇到连续多次取余都是0，那么只需补一个零即可
                if (zero) {
                    zero = false
                    chnstr = chnNumChar[v] + chnstr
                }
            } else {
                zero = true // 第一次取余之后，如果再次取余为零，则需要补零
                str = chnNumChar[v]
                str += chnUnitChar[count]
                chnstr = str + chnstr
            }
            count++
            section = Math.floor(section / 10)
        }
        return chnstr
    }
    // 定义整个数字全部转换的方法，需要依次对数字进行10000为单位的取余
    // 然后分成小节，按小节计算，当每个小节的数不足1000时，则需要进行补零
    let a = numToChn(num)
    num = Math.floor(num)
    let unitPos = 0
    let strIns = ''
    let chnStr = ''
    let needZero = false
    if (num === 0) {
        return chnNumChar[0]
    }
    while (num > 0) {
        let section = num % 10000
        if (needZero) {
            chnStr = chnNumChar[0] + chnStr
        }
        strIns = sectionToChinese(section)
        strIns += (section !== 0) ? chnUnitSection[unitPos] : chnUnitSection[0]
        chnStr = strIns + chnStr
        needZero = (section < 1000) && (section > 0)
        num = Math.floor(num / 10000)
        unitPos++
    }
    return (chnStr + a).replace(/^一十/g, '十')
}
const number2Chinese2 = (num) => {
    const keys = ['零', '一', '二', '三', '四', '五', '六', '七', '八', '九']
    let count = ['', '十', '百', '千']
    let str = ''
    let nums = num.toString().split('').reverse()
    nums.map(function (a, index) {
        str = keys[a] + (a === 0 ? '' : count[index > 3 ? index - 4 : index]) + (index === '4' ? '万' : '') + str
    })
    return str.replace(/(零(?=零))|(零$)|(零(?=万))/g, '')
}
//////////////////////////////////////////////////////////////////////////////
// 金额转换为汉字大写
// 适用场景 不含小数且最多处理到万级别
// =====================
// liuyp 2019年1月22日19:49:31
//////////////////////////////////////////////////////////////////////////////
const money2Chinese = (money) => {
    let cnNums = ['\u96f6', '\u58f9', '\u8d30', '\u53c1', '\u8086', '\u4f0d', '\u9646', '\u67d2', '\u634c', '\u7396']
    let cnIntRadice = ['', '\u62fe', '\u4f70', '\u4edf']
    let cnIntUnits = ['', '\u4e07', '\u4ebf', '\u5146']
    let cnDecUnits = ['\u89d2', '\u5206', '\u6beb', '\u5398']
    let cnInteger = '\u6574'
    let cnIntLast = '\u5706'
    let maxNum = 999999999999999.9999
    let integerNum
    let decimalNum
    let chineseStr = ''
    let parts
    if (money === '') {
        return ''
    }
    money = parseFloat(money)
    if (money >= maxNum) {
        return ''
    }
    if (money === 0) {
        chineseStr = cnNums[0] + cnIntLast + cnInteger
        return chineseStr
    }
    money = money.toString()
    if (money.indexOf('.') === -1) {
        integerNum = money
        decimalNum = ''
    } else {
        parts = money.split('.')
        integerNum = parts[0]
        decimalNum = parts[1].substr(0, 4)
    }
    if (parseInt(integerNum, 10) > 0) {
        let zeroCount = 0
        let IntLen = integerNum.length
        for (let i = 0; i < IntLen; i++) {
            let n = integerNum.substr(i, 1)
            let p = IntLen - i - 1
            let q = p / 4
            let m = p % 4
            if (n === '0') {
                zeroCount++
            } else {
                if (zeroCount > 0) {
                    chineseStr += cnNums[0]
                }
                zeroCount = 0
                chineseStr += cnNums[parseInt(n)] + cnIntRadice[m]
            }
            if (m === 0 && zeroCount < 4) {
                chineseStr += cnIntUnits[q]
            }
        }
        chineseStr += cnIntLast
    }
    if (decimalNum !== '') {
        let decLen = decimalNum.length
        for (let i = 0; i < decLen; i++) {
            let n = decimalNum.substr(i, 1)
            if (n !== '0') {
                chineseStr += cnNums[Number(n)] + cnDecUnits[i]
            }
        }
    }
    if (chineseStr === '') {
        chineseStr += cnNums[0] + cnIntLast + cnInteger
    } else if (decimalNum === '') {
        chineseStr += cnInteger
    }
    return chineseStr
}
//////////////////////////////////////////////////////////////////////////////
// UUID 生成
// =====================
// liuyp 2019年6月9日22:00:05
//////////////////////////////////////////////////////////////////////////////
const uuid = function(len, radix) {
    var chars = '0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'.split(""), uuid = [], i; radix = radix || chars.length;
    if (len) { for (i = 0; i < len; i++) uuid[i] = chars[0 | Math.random() * radix]; }
    else { var r; uuid[8] = uuid[13] = uuid[18] = uuid[23] = '-'; uuid[14] = '4';
    for (i = 0; i < 36; i++) { if (!uuid[i]) { r = 0 | Math.random() * 16; uuid[i] = chars[(i == 19) ? (r & 0x3) | 0x8 : r]; } } }
    return uuid.join("");
}
//////////////////////////////////////////////////////////////////////////////
// 从18位/17位身份证号算出校验位
// (1)十七位数字本体码加权求和公式
// S = Ai * Wi, i = 2, ... , 18
// Y = mod(S, 11)
// i: 表示号码字符从右至左包括校验码字符在内的位置序号
// Ai:表示第i位置上的身份证号码字符值
// Wi:表示第i位置上的加权因子
// i: 18 17 16 15 14 13 12 11 10 9 8 7  6 5 4 3 2 1
// Wi: 7  9 10  5  8  4  2  1  6 3 7 9 10 5 8 4 2 1
// (2)校验码字符值的计算
// Y: 0 1 2 3 4 5 6 7 8 9 10
// 校验码: 1 0 X 9 8 7 6 5 4 3  2
// =====================
// liuyp 2019年9月20日10:58:12
//////////////////////////////////////////////////////////////////////////////
const idCardNumCompute = function( idcardno ){
    //权重值
    var verify18rights = new Array(7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2, 1);
    var verify18_checkdigit = "10X98765432";
    var sum = 0;
    for(var i = 0; i <= 16; i++){ sum += parseInt(idcardno.charAt(i)) * verify18rights[i]; }
    //对权重值取模
    return verify18_checkdigit.charAt(sum % 11);
}
/*================================================================================*\
*  |说明|是否正确的身份证号码
*  |说明|长日期 形如 (2003-12-05 13:04:06)
\*================================================================================*/
const isCardID = function (sId) {
    var iSum = 0,info = "";
    if (!/^\d{17}(\d|x)$/i.test(sId)) return false; //"你输入的身份证长度或格式错误";
    sId = sId.replace(/x$/i, "a");
    var aCity = {
        11: "\u5317\u4eac", 12: "\u5929\u6d25", 13: "\u6cb3\u5317", 14: "\u5c71\u897f", 15: "\u5185\u8499\u53e4",
        21: "\u8fbd\u5b81", 22: "\u5409\u6797", 23: "\u9ed1\u9f99\u6c5f", 31: "\u4e0a\u6d77", 32: "\u6c5f\u82cf",
        33: "\u6d59\u6c5f", 34: "\u5b89\u5fbd", 35: "\u798f\u5efa", 36: "\u6c5f\u897f", 37: "\u5c71\u4e1c",
        41: "\u6cb3\u5357", 42: "\u6e56\u5317", 43: "\u6e56\u5357", 44: "\u5e7f\u4e1c", 45: "\u5e7f\u897f",
        46: "\u6d77\u5357", 50: "\u91cd\u5e86", 51: "\u56db\u5ddd", 52: "\u8d35\u5dde", 53: "\u4e91\u5357",
        54: "\u897f\u85cf", 61: "\u9655\u897f", 62: "\u7518\u8083", 63: "\u9752\u6d77", 64: "\u5b81\u590f",
        65: "\u65b0\u7586", 71: "\u53f0\u6e7e", 81: "\u9999\u6e2f", 82: "\u6fb3\u95e8", 91: "\u56fd\u5916"
    };
    if (aCity[parseInt(sId.substr(0, 2))] == null) return false; //"你的身份证地区非法";
    var sBirthday = sId.substr(6, 4) + "-" + Number(sId.substr(10, 2)) + "-" + Number(sId.substr(12, 2));
    var d = new Date(sBirthday.replace(/-/g, "/"));
    if (sBirthday != (d.getFullYear() + "-" + (d.getMonth() + 1) + "-" + d.getDate())) return false; //"身份证上的出生日期非法";
    for (var i = 17; i >= 0; i--) { iSum += (Math.pow(2, i) % 11) * parseInt(sId.charAt(17 - i), 11) }
    if (iSum % 11 != 1) return false; //"你输入的身份证号非法";
    return true;
}
const cardIDInfo = function (cidstr) {
    if (Object.prototype.toString.call(cidstr)!="[object String]") return null;
    if (cidstr.length != 15 && cidstr.length != 18) return null;
    cidstr = cidstr.replace(/ /g, "").trim();
    var cid = { str: cidstr, length: cidstr.length, sex: "male", city: "" };
    var Wi = [7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2, 1]; // 加权因子
    var ValideCode = [1, 0, 10, 9, 8, 7, 6, 5, 4, 3, 2]; // 身份证验证位值.10代表X
    var aCity = {
        11: "\u5317\u4eac", 12: "\u5929\u6d25", 13: "\u6cb3\u5317", 14: "\u5c71\u897f", 15: "\u5185\u8499\u53e4",
        21: "\u8fbd\u5b81", 22: "\u5409\u6797", 23: "\u9ed1\u9f99\u6c5f", 31: "\u4e0a\u6d77", 32: "\u6c5f\u82cf",
        33: "\u6d59\u6c5f", 34: "\u5b89\u5fbd", 35: "\u798f\u5efa", 36: "\u6c5f\u897f", 37: "\u5c71\u4e1c",
        41: "\u6cb3\u5357", 42: "\u6e56\u5317", 43: "\u6e56\u5357", 44: "\u5e7f\u4e1c", 45: "\u5e7f\u897f",
        46: "\u6d77\u5357", 50: "\u91cd\u5e86", 51: "\u56db\u5ddd", 52: "\u8d35\u5dde", 53: "\u4e91\u5357",
        54: "\u897f\u85cf", 61: "\u9655\u897f", 62: "\u7518\u8083", 63: "\u9752\u6d77", 64: "\u5b81\u590f",
        65: "\u65b0\u7586", 71: "\u53f0\u6e7e", 81: "\u9999\u6e2f", 82: "\u6fb3\u95e8", 91: "\u56fd\u5916"
    };
    if (!IdCardValidate(cidstr)) return null;
    cid.city = cityByIdCard(cidstr);
    if (!cid.city) return null;
    cid.sex = maleOrFemalByIdCard(cidstr);
    //验证身份证号码
    function IdCardValidate(idCard) {
        if (idCard.length == 15) {
            return isValidityBrithBy15IdCard(idCard); //进行15位身份证的验证
        } else if (idCard.length == 18) {
            var a_idCard = idCard.split(""); // 得到身份证数组
            return (isValidityBrithBy18IdCard(idCard) && isTrueValidateCodeBy18IdCard(a_idCard)) //进行18位身份证的基本验证和第18位的验证
        }
        return false;
    }
    //判断身份证号码为18位时最后的验证位是否正确
    function isTrueValidateCodeBy18IdCard(a_idCard) {
        var sum = 0; // 声明加权求和变量
        if (a_idCard[17].toLowerCase() == 'x') {
            a_idCard[17] = 10; // 将最后位为x的验证码替换为10方便后续操作
        }
        for (var i = 0; i < 17; i++) {
            sum += Wi[i] * a_idCard[i]; // 加权求和
        }
        var valCodePosition = sum % 11; // 得到验证码所位置
        return (a_idCard[17] == ValideCode[valCodePosition]);
    }
    //验证18位数身份证号码中的生日是否是有效生日
    function isValidityBrithBy18IdCard(idCard18) {
        var year = idCard18.substring(6, 10);
        var month = idCard18.substring(10, 12);
        var day = idCard18.substring(12, 14);
        var temp_date = new Date(year, parseFloat(month) - 1, parseFloat(day));
        // 这里用getFullYear()获取年份 避免千年虫问题
        return !(temp_date.getFullYear() != parseFloat(year) || temp_date.getMonth() != parseFloat(month) - 1 || temp_date.getDate() != parseFloat(day));
    }
    //验证15位数身份证号码中的生日是否是有效生日
    function isValidityBrithBy15IdCard(idCard15) {
        var year = idCard15.substring(6, 8);
        var month = idCard15.substring(8, 10);
        var day = idCard15.substring(10, 12);
        var temp_date = new Date(year, parseFloat(month) - 1, parseFloat(day));
        // 对于老身份证中的你年龄则不需考虑千年虫问题而使用getYear()方法
        return !(temp_date.getYear() != parseFloat(year) || temp_date.getMonth() != parseFloat(month) - 1 || temp_date.getDate() != parseFloat(day));
    }
    //通过身份证判断是男是女
    function maleOrFemalByIdCard(idCard) {
        if (idCard.length == 15) {
            return (idCard.substring(14, 15) % 2 == 0) ? 'female' : 'male';
        } else if (idCard.length == 18) {
            return (idCard.substring(14, 17) % 2 == 0) ? 'female' : 'male';
        }
        return null;
    }
    //通过身份证判断区域
    function cityByIdCard(idCard) {
        return aCity[parseInt(cidstr.substr(0, 2))] || null;
    }
    return cid;
}
//////////////////////////////////////////////////////////////////////////////
// 数字估算
// 源,过滤数组
// 为了适应浏览器局限宽度来显示数字,需要对数字进行估算显示
// =====================
// liuyp 2019年9月20日10:58:12
//////////////////////////////////////////////////////////////////////////////
const estimate = function(n,s){
    s=s || 4;if(s<5) s = 5;n = parseInt(n);
    function getNum(nl,u){
        var jqxsd = 2 - 2 * (u.length), zs = n / nl;
        if(zs > 1e3){jqxsd = 0}
        else if(zs > 100){if(u.length){jqxsd = 0}else{jqxsd = 1}}
        else if(zs > 10){if(u.length){jqxsd = 0}else{jqxsd = 2}}
        else{if(u.length){jqxsd = 1}else{jqxsd = 2}}
        return Number(zs.toFixed(jqxsd)) + u
    }
    if(n > 1e8){return getNum(1e8, "亿")}
    else if(n > 1e4){return getNum(1e4, "万")}
    return getNum(1, "")
}
//////////////////////////////////////////////////////////////////////////////
// 字节计算
// =====================
// liuyp 2019年9月20日10:58:12
//////////////////////////////////////////////////////////////////////////////
const parseBytes = function(e/*值*/, t/*单位*/, n/*计算方式*/, r/*保留位数*/) {
    var i, spcr; return t = t != "KBytes" && t != "MBytes" && t != "GBytes" && t != "TBytes" ? "mixed": t,
    spcr = n == null || n == 0 ? " ": "",
    t == "mixed" && e > 1099511627776 || t == "TBytes" ? i = (e / 1099511627776).toFixed(r || 3)
    + spcr + (n ? 'TB': 'TBytes') : t == "mixed" && e > 1073741824 || t == "GBytes" ?
    i = (e / 1073741824).toFixed(r || 3) + spcr + (n ? 'GB': 'GBytes') : t == "mixed" &&
    e > 1048576 || t == "MBytes" ? i = (e / 1048576).toFixed(r || 3) + spcr + (n ? 'MB': 'MBytes')
    : i = (e / 1024).toFixed(r || 3) + spcr + (n ? 'KB': 'KBytes'),i
}
//////////////////////////////////////////////////////////////////////////////
// 小数点后位数
// =====================
// liuyp 2019年9月20日10:58:12
//////////////////////////////////////////////////////////////////////////////
const places = function(n) {
    try { return n.toString().split('.')[1].length }
    catch (e) { return 0 }
};
//////////////////////////////////////////////////////////////////////////////
// 剔除小数点
// =====================
// liuyp 2019年9月20日10:58:12
//////////////////////////////////////////////////////////////////////////////
const no_places = function(n) {
    return Number(n.toString().replace(".", ""));
};
//////////////////////////////////////////////////////////////////////////////
// 精准加法
// 对于整数，前端出现问题的几率可能比较低，毕竟很少有业务需要需要用到超大整数，只要运算结果不超过 Math.pow(2, 53) 就不会丢失精度。
// 对于小数，前端出现问题的几率还是很多的，尤其在一些电商网站涉及到金额等数据。解决方式：把小数放到位整数（乘倍数），再缩小回原来倍数（除倍数）
// =====================
// liuyp 2019年9月20日10:58:12
//////////////////////////////////////////////////////////////////////////////
const add = function( /*arguments*/ ) {
    var args = [], l = arguments.length;
    if (l == 0) { return 0 }
    for (var i = 0; i < l; i++) { args[i] = parseFloat(arguments[i]) }
    if (l == 1) { return args[0] }
    var m = 0, r2 = 0;
    for (var i = 0; i < l; i++) { m = Math.max( places(args[i]), m) }
    m = Math.pow(10, m);
    for (var i = 0; i < l; i++) { r2 += args[i] * m }
    return r2 / m;
};
//////////////////////////////////////////////////////////////////////////////
// 精准减法
// =====================
// liuyp 2019年9月20日10:58:12
//////////////////////////////////////////////////////////////////////////////
const sub = function( /*arguments*/ ) {
    var l = arguments.length;
    if (l == 0) { return 0 }
    var args = [];
    for (var i = 0; i < l; i++) { args[i] = parseFloat(arguments[i]) }
    if (l == 1) { return args[0] }
    var m = 0, r2 = args[0];
    for (var i = 0; i < l; i++) { m = Math.max( places(args[i]), m) }
    m = Math.pow(10, m);
    r2 *= m;
    for (var i = 1; i < l; i++) { r2 -= args[i] * m }
    return r2 / m;
};
//////////////////////////////////////////////////////////////////////////////
// 精准乘法
// =====================
// liuyp 2019年9月20日10:58:12
//////////////////////////////////////////////////////////////////////////////
const mul = function( /*arguments*/ ) {
    var args = [];
    var l = arguments.length;
    if (l == 0) { return 0 }
    if (l == 1) { return parseFloat(arguments[0]) }
    var m = 0, r2 = 1;
    for (var i = 0; i < l; i++) {
        var n = parseFloat(arguments[i]).toString();
        var r1 =  places(n);
        args[i] = n;
        m += r1;
    }
    m = Math.pow(10, m);
    for (var i = 0; i < l; i++) { r2 *= Number(args[i].replace(/\./g, "")) }
    return r2 / m;
};
//////////////////////////////////////////////////////////////////////////////
// 精准除法
// =====================
// liuyp 2019年9月20日10:58:12
//////////////////////////////////////////////////////////////////////////////
const div = function( bcs, cs ) {
    bcs = parseFloat(bcs);
    cs = parseFloat(cs);
    var t1 = places(bcs),
        t2 = places(cs),
        r1 = no_places(bcs),
        r2 = no_places(cs);
    return mul((r1 / r2), Math.pow(10, t2 - t1));
};
//////////////////////////////////////////////////////////////////////////////
// 数字精准比较
// =====================
// liuyp 2019年9月20日10:58:12
//////////////////////////////////////////////////////////////////////////////
const parseFloatRemoveNotNum = function( s ) {
    return parseFloat( s.replace( /[^0-9\.]/g, '') || 0 );
};
const compare = function( a, b ) {
    var ax = parseFloatRemoveNotNum( a );
    var bx = parseFloatRemoveNotNum( b );
    if( ax == bx ){ return '=' }
    else { return ax > bx ? '>' : '<' }
};
const gt = function( a, b ) { var r = compare( a, b ); return r == '>' };
const gte = function( a, b ) { var r = compare( a, b ); return r == '>' || r == '=' };
const lt = function( a, b ) { var r = compare( a, b ); return r == '<' };
const lte = function( a, b ) { var r = compare( a, b ); return r == '<' || r == '=' };
const eq = function( a, b ) { var r = compare( a, b ); return r == '=' };
/*================================================================================*\
*  |说明|_NUMBER_IP地址的校验
*  |参数|源
\*================================================================================*/
const validateIP = function (e) {
    var t = 0;
    if (e == "0.0.0.0") t = 1;
    else if (e == "255.255.255.255") t = 2;
    else {
        var n = e.match(/^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/);
        if (n == null) t = 5;
        else for (var field = 1; field <= 4; field++) n[field] > 255 && (t = 4),
        n[field] == 255 && field == 4 && (t = 3)
    } return t > 0
}
/*================================================================================*\
*  |说明|_NUMBER_IP地址的比较
*  |参数|源
\*================================================================================*/
const compareIP = function (ipBegin, ipEnd) {
    var temp1 = ipBegin.split("."),
        temp2 = ipEnd.split(".");
    for (var i = 0; i < 4; i++) {
        if (temp1[i]>temp2[i]) { return 1; }
        else if (temp1[i]<temp2[i]) { return -1; }
    } return 0;
}
exports = module.exports = {
    uuid,
    idCardNumCompute,
    estimate,
    parseBytes,
    add,
    sub,
    mul,
    div,
    gt,
    gte,
    lt,
    lte,
    eq,
    validateIP,
    compareIP,
    isCardID,
    cardIDInfo
}
